use derive_builder::Builder;
use serde::{Deserialize, Serialize};

use crate::mapper::ValueMapper;
use crate::modeling::{
    Class, ClassAttribute, ClassAttributeBuilder, ClassAttributePersistence, ClassBuilder,
    ClassGetter, ClassPersistence, ClassPersistenceBuilder, ClassPersistenceGetter, ClassRelation,
    ClassRelationGetter, ValueType,
};
use crate::{error::Error, modeling::Value};

pub const DATASOURCE_CLASS_RELATION_MAPPING_CLASS_IDENTIFIER: &'static str =
    "g01_datasource_class_relation_mappings";

pub const DATASOURCE_CLASS_RELATION_MAPPING_CLASS_NAME: &'static str =
    "datasource_class_relation_mappings";

pub const DATASOURCE_CLASS_RELATION_MAPPING_TABLE_NAME: &'static str =
    "_g01_meta_datasource_class_relation_mappings";

#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
pub enum RelationMappingType {
    #[default]
    #[serde(rename = "inner_keys")]
    InnerKeys,
    #[serde(rename = "outter_table")]
    OutterTable,
}

#[derive(Debug, Serialize, Deserialize, Default, Clone, Builder, PartialEq)]
#[builder(name = "DataSourceClassRelationMappingBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct DataSourceClassRelationMapping {
    //
    pub id: i64,
    //
    pub datasource_id: i64,
    //
    pub relation_identifier: String,

    pub src_identifier: String,

    pub target_identifier: String,

    pub mapping_type: RelationMappingType,

    pub mapping_value: String,

    pub src_keys: String,

    pub target_keys: String,

    pub options: String,

    pub description: String,
}

//
crate::impl_from_value!(DataSourceClassRelationMapping);
crate::impl_class_persistence_getter!(
    DataSourceClassRelationMapping,
    DATASOURCE_CLASS_RELATION_MAPPING_TABLE_NAME
);
crate::impl_class_relations_getter!(DataSourceClassRelationMapping, vec![]);

impl ClassGetter for DataSourceClassRelationMapping {
    fn get_name() -> anyhow::Result<String> {
        Ok(String::from(DATASOURCE_CLASS_RELATION_MAPPING_CLASS_NAME))
    }

    fn get_identifier() -> anyhow::Result<String> {
        Ok(String::from(
            DATASOURCE_CLASS_RELATION_MAPPING_CLASS_IDENTIFIER,
        ))
    }
    fn get_class() -> anyhow::Result<Class> {
        let mut attribute_builder = ClassAttributeBuilder::default();
        attribute_builder.class_identifier(DATASOURCE_CLASS_RELATION_MAPPING_CLASS_IDENTIFIER);

        let mut commons = super::common_attributes_with_builder(&attribute_builder)?;
        let mut append = vec![
            attribute_builder
                .clone()
                .name("datasource_id")
                .title("数据源")
                .data_type(ValueType::Number)
                .build()?,
            attribute_builder
                .clone()
                .name("relation_identifier")
                .title("关系")
                .data_type(ValueType::String)
                .build()?,
            attribute_builder
                .clone()
                .name("src_identifier")
                .title("源类型")
                .data_type(ValueType::String)
                .build()?,
            attribute_builder
                .clone()
                .name("target_identifier")
                .title("目标类型")
                .data_type(ValueType::String)
                .build()?,
            attribute_builder
                .clone()
                .name("mapping_type")
                .title("mapping_type")
                .data_type(ValueType::String)
                .build()?,
            attribute_builder
                .clone()
                .name("src_keys")
                .title("src_keys")
                .data_type(ValueType::String)
                .build()?,
            attribute_builder
                .clone()
                .name("mapping_value")
                .title("表名称")
                .data_type(ValueType::String)
                .build()?,
            attribute_builder
                .clone()
                .name("target_keys")
                .title("target_keys")
                .data_type(ValueType::String)
                .build()?,
        ];
        append.append(&mut commons);
        append.sort_by(|a, b| a.position.cmp(&b.position));
        let clazz = ClassBuilder::default()
            .identifier(DATASOURCE_CLASS_RELATION_MAPPING_CLASS_IDENTIFIER)
            .name(DATASOURCE_CLASS_RELATION_MAPPING_CLASS_NAME)
            .data_is_type(false)
            .is_persistent(true)
            .primary_key(String::from("identifier"))
            .attributes(append)
            .build()?;
        Ok(clazz)
    }
}
